/**
 * @file xdisk.c
 * @author zhao yijing
 * @brief  disk层
 * @version 0.1
 * @date 2022-11-03
 *
 * @copyright Copyright (c) 2022
 *
 */
#include "xdisk.h"
#include "xtypes.h"

uint8_t temp_buf[512] = {0};

static xfat_err_t disk_get_extend_part_count(xdisk_t *disk,
                                             uint32_t start_sector,
                                             uint32_t *ext_count);

static xfat_err_t disk_get_extend_part(xdisk_t *disk,
                                       xdisk_part_t *disk_part,
                                       uint32_t start_sector,
                                       int part_no,
                                       uint32_t *count);

/**
 * @brief  打开 初始化磁盘设备
 * @param  disk              [in/out]磁盘设备
 * @param  name              设备名称
 * @param  driver            磁盘驱动
 * @param  init_data         初始化参数
 * @return xfat_err_t        打开成功 - (0)
 */
xfat_err_t xdisk_open(xdisk_t *disk,
                      const char *name,
                      xdisk_driver_t *driver,
                      void *init_data)
{
    xfat_err_t err = 0;

    disk->driver = driver;
    err = disk->driver->open(disk, init_data);
    if (err != 0)
    {
        return err;
    }
    disk->name = name;
    return FS_ERR_OK;
}

/**
 * @brief  关闭磁盘设备
 * @param  disk              磁盘设备
 * @return xfat_err_t        关闭成功 - (0)
 */
xfat_err_t xdisk_close(xdisk_t *disk)
{
    xfat_err_t err = 0;
    err = disk->driver->close(disk);
    if (err != 0)
    {
        return err;
    }
    return FS_ERR_OK;
}

/**
 * @brief  向指定扇区写数据
 * @param  disk              磁盘设备
 * @param  buf               写入数据缓冲
 * @param  start_sector      写入起始扇区
 * @param  count             写入扇区数
 * @return xfat_err_t        写入成功 - (0)
 */
xfat_err_t xdisk_write_sector(xdisk_t *disk,
                              uint8_t *buf,
                              uint32_t start_sector,
                              uint32_t count)
{
    xfat_err_t err = 0;
    if (start_sector + count > disk->totle_sector)
    {
        return FS_ERR_PARAM;
    }

    err = disk->driver->write_sector(disk, buf, start_sector, count);
    if (err != 0)
    {
        return err;
    }
    return FS_ERR_OK;
}

/**
 * @brief  从指定扇区读取数据
 * @param  disk              磁盘设备
 * @param  buf               读取数据缓冲区
 * @param  start_sector      读取数据起始扇区
 * @param  count             需要读取的扇区数
 * @return xfat_err_t        读取成功 - (0)
 */
xfat_err_t xdisk_read_sector(xdisk_t *disk,
                             uint8_t *buf,
                             uint32_t start_sector,
                             uint32_t count)
{
    xfat_err_t err = 0;
    if (start_sector + count > disk->totle_sector)
    {
        return FS_ERR_PARAM;
    }

    err = disk->driver->read_sector(disk, buf, start_sector, count);
    if (err != 0)
    {
        return err;
    }
    return FS_ERR_OK;
}

/**
 * @brief  获取分区数量
 * @param  disk              磁盘设备
 * @param  count             [out]磁盘数量
 * @return xfat_err_t        成功 - (0)
 */
xfat_err_t xdisk_get_part_count(xdisk_t *disk, uint32_t *count)
{
    uint8_t *disk_buf = temp_buf;
    int err = 0;
    mbr_part_t *part = NULL;
    int r_count = 0;
    uint8_t extend_part_flsg = 0;   // 扩展分区标志
    uint32_t start_sector[4] = {0}; // 记录扩展分区起始扇区
    uint32_t ext_part_count = 0;    // 扩展分区数量

    // 读取第一个扇区MBR信息
    err = disk->driver->read_sector(disk, disk_buf, 0, 1);
    if (err < 0)
    {
        return err;
    }

    // 解析主分区数量
    part = ((mbr_t *)disk_buf)->part_info;
    for (int i = 0; i < MBR_PRIMARY_PART_NR; i++, part++)
    {
        if (part->system_id == FS_NOT_VALID)
        {
            // 无效分区类型
            continue;
        }
        else if (part->system_id == FS_EXTEND)
        {
            // 记录扩展分区
            start_sector[i] = part->relative_sector;
            extend_part_flsg |= (1 << i);
        }
        else
        {
            // 记录主分区数
            r_count++;
        }
    }

    // 解析扩展分区数量
    if (extend_part_flsg != 0)
    {
        // 说明有扩展分区 需要解析
        for (int i = 0; i < MBR_PRIMARY_PART_NR; i++)
        {
            if ((extend_part_flsg & (1 << i)) != 0)
            {
                // 解析扩展分区
                err = disk_get_extend_part_count(disk,
                                                 start_sector[i],
                                                 &ext_part_count);
                if (err < 0)
                {
                    return err;
                }
                r_count += ext_part_count;
            }
        }
    }

    *count = r_count;
    return FS_ERR_OK;
}

/**
 * @brief  获取分区信息
 * @param  disk              磁盘设备
 * @param  xdisk_part        [out]分区信息
 * @param  part_no           分区标号
 * @return xfat_err_t        成功 - (0)
 */
xfat_err_t xdisk_get_part(xdisk_t *disk,
                          xdisk_part_t *xdisk_part,
                          int part_no)
{
    uint8_t *disk_buf = temp_buf;
    int err = 0;
    mbr_part_t *mbr_part = NULL;
    uint32_t curr_no = 0;
    uint32_t ext_count = 0;

    // 获取MBR信息
    err = disk->driver->read_sector(disk, disk_buf, 0, 1);
    if (err < 0)
    {
        return err;
    }

    // 遍历主分区表
    mbr_part = ((mbr_t *)disk_buf)->part_info;
    for (int i = 0; i < MBR_PRIMARY_PART_NR; i++, mbr_part++)
    {
        if (mbr_part->system_id == FS_NOT_VALID)
        {
            // 无效分区 跳过
            continue;
        }
        else if (mbr_part->system_id == FS_EXTEND)
        {
            // 解析逻辑分区
            err = disk_get_extend_part(disk,
                                       xdisk_part,
                                       mbr_part->relative_sector,
                                       part_no - i,
                                       &ext_count);
            if (err < 0)
            {
                return err;
            }

            if (err == FS_ERR_OK)
            {
                // 找到分区
                return FS_ERR_OK;
            }
            else
            {
                // 没有找到分区
                curr_no += ext_count;
                err = disk->driver->read_sector(disk, disk_buf, 0, 1);
                if (err < 0)
                {
                    return err;
                }
            }
        }
        else
        {
            // 解析主分区
            if (curr_no == part_no)
            {
                xdisk_part->type = mbr_part->system_id;
                xdisk_part->start_sector = mbr_part->relative_sector;
                xdisk_part->total_sector = mbr_part->total_sector;
                xdisk_part->disk = disk;
                return FS_ERR_OK;
            }
            curr_no++;
        }
    }

    return FS_ERR_NONE;
}

/**
 * @brief  解析扩展分区数量
 * @param  disk              磁盘设备
 * @param  start_sector      扩展分区起始扇区
 * @param  ext_count         扩展分区数量
 * @return xfat_err_t        成功 - (0)
 */
static xfat_err_t disk_get_extend_part_count(xdisk_t *disk,
                                             uint32_t start_sector,
                                             uint32_t *ext_count)
{
    int r_ext_count = 0;
    uint8_t *disk_buf = temp_buf;
    uint32_t ext_start_sector = start_sector;
    uint32_t start_sector_t = start_sector;
    mbr_part_t *ext_disk_part = NULL;
    int err = 0;

    while (1)
    {
        err = disk->driver->read_sector(disk,
                                        disk_buf,
                                        start_sector_t,
                                        1);
        if (err < 0)
        {
            return err;
        }

        ext_disk_part = ((mbr_t *)disk_buf)->part_info;
        if (ext_disk_part->system_id == FS_NOT_VALID)
        {
            // 无效分区
            // break;
            continue;
        }
        r_ext_count++;

        ext_disk_part++;
        if (ext_disk_part->system_id != FS_EXTEND)
        {
            // 无后续分区 退出
            break;
        }

        start_sector_t = ext_start_sector + ext_disk_part->relative_sector;
    }

    *ext_count = r_ext_count;
    return FS_ERR_OK;
}

/**
 * @brief  获取扩展分区信息
 * @param  disk              磁盘设备
 * @param  disk_part         扩展分区信息
 * @param  start_sector      扩展分区起始扇区
 * @param  part_no           扩展分区号
 * @param  count             扩展分区数量
 * @return xfat_err_t        成功 - (0)
 */
static xfat_err_t disk_get_extend_part(xdisk_t *disk,
                                       xdisk_part_t *disk_part,
                                       uint32_t start_sector,
                                       int part_no,
                                       uint32_t *count)
{
    uint8_t *disk_buf = temp_buf;
    int err = 0;
    uint32_t start_sec_t = start_sector;
    uint32_t ext_start_sector = start_sector;
    mbr_part_t *ext_part = NULL;
    uint32_t curr_no = 0;

    while (1)
    {
        // 获取扩展分区MBR信息
        err = disk->driver->read_sector(disk, disk_buf, start_sec_t, 1);
        if (err < 0)
        {
            return err;
        }

        ext_part = ((mbr_t *)disk_buf)->part_info;
        if (ext_part->system_id == FS_NOT_VALID)
        {
            err = FS_ERR_EOF;
            break;
        }

        if (curr_no == part_no)
        {
            disk_part->type = ext_part->system_id;
            disk_part->start_sector = start_sec_t + ext_part->relative_sector;
            disk_part->total_sector = ext_part->total_sector;
            disk_part->disk = disk;
            break;
        }
        curr_no++;

        // 该扩展分区没有后续
        ext_part++;
        if (ext_part->system_id != FS_EXTEND)
        {
            err = FS_ERR_EOF;
            break;
        }

        start_sec_t = ext_start_sector + ext_part->relative_sector;
    }

    *count = curr_no;
    return err;
}
